using System.Linq;
using Tycho.Parser.Peg;

namespace Tycho.Grammar {
    public class GrammarCompiler : IProductionCompiler {
        private readonly IProductionCompiler NonTerminalCompiler;
        private readonly IProductionCompiler AnonymousNonTerminalCompiler;
        private readonly IProductionCompiler GrammarProductionCompiler;
        private readonly IProductionCompiler DynamicChoiceCompiler;
        private readonly IProductionCompiler StringCompiler;
        private readonly IProductionCompiler TermMacroCompiler;

        public GrammarCompiler () {
            var ruleCompiler = new RuleCompiler (this);
            var nonTerminalRuleCompiler = new NonTerminalRuleCompiler (ruleCompiler);
            NonTerminalCompiler = new NonTerminalCompiler (nonTerminalRuleCompiler);
            GrammarProductionCompiler = new GrammarProductionCompiler (this);
            DynamicChoiceCompiler = new DynamicChoiceCompiler (this, new ChoiceAssociativityRewriter ());
            StringCompiler = new StringCompiler (this);
            TermMacroCompiler = new TermMacroCompiler (nonTerminalRuleCompiler);
            AnonymousNonTerminalCompiler = new AnonymousNonTerminalCompiler (ruleCompiler);
        }

        public struct GrammarCompilerParams {
            public IProductionCompiler NonTerminalCompiler;
            public IProductionCompiler GrammarProductionCompiler;
            public IProductionCompiler DynamicChoiceCompiler;
            public IProductionCompiler StringCompiler;
            public IProductionCompiler TermMacroCompiler;
            public IProductionCompiler AnonymousNonTerminalCompiler;
        }

        public GrammarCompiler (GrammarCompilerParams grammarCompilerParams) {
            NonTerminalCompiler = grammarCompilerParams.NonTerminalCompiler;
            GrammarProductionCompiler = grammarCompilerParams.GrammarProductionCompiler;
            DynamicChoiceCompiler = grammarCompilerParams.DynamicChoiceCompiler;
            StringCompiler = grammarCompilerParams.StringCompiler;
            TermMacroCompiler = grammarCompilerParams.TermMacroCompiler;
            AnonymousNonTerminalCompiler = grammarCompilerParams.AnonymousNonTerminalCompiler;
        }

        public GrammarTree Compile (ITerm term, ChoicePrecedence precedence, ProductionCompilerContext context) {
            var productionTerm = term as CompositeTerm;

            if (productionTerm != null) {
                switch (productionTerm.Name) {
                    case "non-terminal":
                        return NonTerminalCompiler.Compile (productionTerm, precedence, context);
                    case "anonymous-non-terminal":
                        return AnonymousNonTerminalCompiler.Compile (productionTerm, precedence, context);
                    case "string":
                        return StringCompiler.Compile (productionTerm, precedence, context);
                    case "quote":
                        return new GrammarTree (CompileQuote (productionTerm, context));
                    case "dynamic-choice":
                        return DynamicChoiceCompiler.Compile (productionTerm, precedence, context);
                    case "grammar":
                        return GrammarProductionCompiler.Compile (productionTerm, precedence, context);
                    case "term-macro":
                        return TermMacroCompiler.Compile (productionTerm, precedence, context);
                }
            } else if (term is IdentifierTerm) {
                var identifierTerm = (IdentifierTerm) term;

                return new GrammarTree (context.Lookup.Lookup (identifierTerm.Name));
            }

            throw new RuleNotRecognisedException ();
        }

        private static IProduction CompileQuote (CompositeTerm productionTerm, ProductionCompilerContext context) {
            return context.Environment.EvaluateQuote (productionTerm.SubTerms ["expression"]);
        }
    }
}